package com.xiaogang.service;

import cn.hutool.core.io.FileUtil;
import com.xiaogang.model.JzyhPrintParam;
import com.xiaogang.utils.APIRs;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.io.RandomAccessReadBufferedFile;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.imageio.ImageIO;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.Copies;
import javax.print.attribute.standard.MediaSizeName;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.Printable;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @ProjectName : jzyhPrint
 * @作者 : 侯小刚
 * @描述 :
 * @创建日期 : 2025/10/14 15:19
 */
@Slf4j
@Service
public class JzyhPrintService {

    private static final Map<String, String> MAGIC_MAP = new HashMap<>();
    static {
        MAGIC_MAP.put("%PDF", "pdf");
        MAGIC_MAP.put("\u0089PNG", "png");
        MAGIC_MAP.put("\u00FF\u00D8\u00FF", "jpg");
        MAGIC_MAP.put("GIF8", "gif");
    }

    public APIRs jzyhPrintFile(JzyhPrintParam param) {
        List<String> urls = param.getUrls();
        if(urls.isEmpty()) {
            return APIRs.fail("There are no images to print");
        }
        PrintService selectedPrinter = null;
        PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null);
        List<String> printerNameList = Arrays.stream(printServices).map(PrintService::getName).collect(Collectors.toList());
        String printerName = getPrinterByConfig();
        if(!StringUtils.hasLength(printerName)) {
            return new APIRs(205, "Please select a printer", printerNameList);
        }

        if(!printerNameList.contains(printerName)) {
            return new APIRs(206, "Please select a printer", printerNameList);
        }

        for (PrintService ps : printServices) {
            if (ps.getName().contains(printerName)) {
                selectedPrinter = ps;
                break;
            }
        }

        try{
            boolean isPrintSuccess = true;
            for (String url : urls) {
                File tempFile;

                // 检测是否为base64字符串，如果是则按base64处理
                if (isBase64(url)) {
                    tempFile = convertBase64ToFile(url);
                } else {
                    // 如果不是base64，则按URL处理
                    tempFile = downloadImage(url);
                }

                // 如果不是pdf或者图片，则不打印
                if(!(tempFile.getName().endsWith(".pdf") || tempFile.getName().endsWith(".png")
                        || tempFile.getName().endsWith(".jpg"))) {
                    tempFile.delete();
                    isPrintSuccess = false;
                    continue;
                }
                printFileSilently(tempFile, selectedPrinter);
                tempFile.delete();
            }
            if(isPrintSuccess) {
                return APIRs.successMsg("It has been sent to the printer");
            } else {
                return APIRs.successMsg("Some files cannot be printed");
            }
        } catch (Exception e) {
            log.error("Print error: {}", e);
            return APIRs.fail(e.getMessage());
        }
    }

    /**
     * 检测字符串是否为base64格式
     * @param str 待检测的字符串
     * @return true表示是base64格式，false表示不是
     */
    private boolean isBase64(String str) {
        if (str == null || str.trim().isEmpty()) {
            return false;
        }

        // 检查是否包含base64 data URL前缀
        if (str.startsWith("data:image/") || str.startsWith("data:application/pdf")) {
            return true;
        }

        // 移除可能的data URL前缀
        String base64Str = str;
        if (str.contains(",")) {
            base64Str = str.substring(str.indexOf(",") + 1);
        }

        // 检查base64格式：只包含base64字符且长度合理
        String base64Pattern = "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$";
        return base64Str.matches(base64Pattern) && base64Str.length() > 100;
    }

    /**
     * 将base64字符串转换为临时文件
     * @param base64Str base64字符串
     * @return 临时文件
     * @throws Exception 转换异常
     */
    public static File convertBase64ToFile(String base64Str) throws Exception {
        // 解析data URL格式
        String base64Data = base64Str;
        String suffix = "jpg"; // 默认后缀

        if (base64Str.contains(",")) {
            String[] parts = base64Str.split(",", 2);
            if (parts.length == 2) {
                base64Data = parts[1];
                String mimeType = parts[0];

                // 根据MIME类型确定后缀
                if (mimeType.contains("png")) {
                    suffix = "png";
                } else if (mimeType.contains("jpeg") || mimeType.contains("jpg")) {
                    suffix = "jpg";
                } else if (mimeType.contains("gif")) {
                    suffix = "gif";
                } else if (mimeType.contains("pdf")) {
                    suffix = "pdf";
                }
            }
        }

        // 解码base64数据
        byte[] fileBytes = Base64.getDecoder().decode(base64Data);

        // 如果没有从MIME类型确定后缀，尝试从文件头识别
        if ("jpg".equals(suffix)) {
            suffix = detectFileSuffixForBase64(fileBytes);
            if (suffix == null) {
                suffix = "jpg"; // 默认使用jpg
            }
        }

        // 创建临时文件
        String workDir = System.getProperty("user.dir");
        File dir = new File(workDir, "image");
        if (!dir.exists()) dir.mkdirs();

        File tempFile = new File(dir, "tmp_print_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 1000) + "." + suffix);
        Files.write(tempFile.toPath(), fileBytes);

        log.info("base64字符串已转换为临时文件：" + tempFile.getAbsolutePath() + "，后缀：" + suffix);
        return tempFile;
    }

    /**
     * 从文件字节数组中检测文件类型
     * @param fileBytes 文件字节数组
     * @return 文件后缀
     */
    private static String detectFileSuffixForBase64(byte[] fileBytes) {
        if (fileBytes.length >= 8) {
            byte[] header = new byte[Math.min(8, fileBytes.length)];
            System.arraycopy(fileBytes, 0, header, 0, header.length);

            try {
                String h = new String(header, "ISO-8859-1");
                for (Map.Entry<String, String> entry : MAGIC_MAP.entrySet()) {
                    if (h.startsWith(entry.getKey())) return entry.getValue();
                }
            } catch (Exception e) {
                log.warn("文件头检测失败", e);
            }
        }
        return null;
    }

    /**
     * 获取打印机
     * @return
     */
    private String getPrinterByConfig() {
        String workDir = System.getProperty("user.dir");
        String printerConfig = workDir + "\\printerConfig.txt";

        StringBuffer sb = new StringBuffer();
        File file = new File(printerConfig);
        if(!file.exists()) {
            FileUtil.touch(file);
        }
        List<String> lines = FileUtil.readUtf8Lines(file);
        // 输出文件内容
        for (String line : lines) {
            if (!line.trim().isEmpty()) { // 删除空行
                sb.append(line);
            }
        }
        if(sb.length()>0) {
            return sb.toString();
        }
        return "";
        //FileUtil.writeUtf8String(sb.toString(), "C:\\Users\\paji\\Desktop\\大坪医院\\新建 文本文档1.txt");
    }

    // 下载网络图片到临时文件
    public static File downloadImage(String imageUrl) throws Exception {
        URL url = new URL(imageUrl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(20000);
        conn.setReadTimeout(20000);
        conn.setRequestMethod("GET");

        // 尝试从Content-Type获取后缀
        String suffix = null;
        String contentType = conn.getContentType();
        if (contentType != null) {
            if (contentType.contains("pdf")) suffix = "pdf";
            else if (contentType.contains("png")) suffix = "png";
            else if (contentType.contains("jpeg")) suffix = "jpg";
            else if (contentType.contains("jpg")) suffix = "jpg";
            else if (contentType.contains("gif")) suffix = "gif";
        }

        // 下载文件到临时文件（先无后缀）
        String workDir = System.getProperty("user.dir");
        File dir = new File(workDir, "image");
        if (!dir.exists()) dir.mkdirs();
        File tempFile = new File(dir, "tmp_print_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 1000));
        try (InputStream in = conn.getInputStream()) {
            Files.copy(in, tempFile.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
        }

        // 如果Content-Type没有识别到，再用魔数识别
        if (suffix == null) {
            byte[] header = new byte[8];
            try (InputStream is = Files.newInputStream(tempFile.toPath())) {
                int read = is.read(header, 0, 8);
                if (read > 0) {
                    suffix = detectFileSuffix(header);
                }
            }
        }

        // 如果仍然无法识别，使用默认后缀
        if (suffix == null) suffix = "bin";

        // 重命名临时文件加上后缀
        File finalFile = new File(tempFile.getParentFile(), tempFile.getName() + "." + suffix);
        tempFile.renameTo(finalFile);

        log.info("文件已下载到临时文件：" + finalFile.getAbsolutePath() + "，后缀：" + suffix);
        return finalFile;
    }

    private static String detectFileSuffix(byte[] header) {
        if (header.length >= 4) {
            String h = new String(header);
            for (Map.Entry<String, String> entry : MAGIC_MAP.entrySet()) {
                if (h.startsWith(entry.getKey())) return entry.getValue();
            }
        }
        return null;
    }


    /**
     * 静默打印图片或 PDF（自动根据图片长宽方向旋转）
     * @param file 要打印的文件（图片或 PDF）
     * @param selectedPrinter 指定打印机
     * @throws Exception 打印异常
     */
    public static void printFileSilently(File file, PrintService selectedPrinter) throws Exception {
        String lowerName = file.getName().toLowerCase();
        PrinterJob job = PrinterJob.getPrinterJob();
        job.setPrintService(selectedPrinter);

        // ===== 读取文件为 BufferedImage 数组 =====
        BufferedImage[] pages;
        if (lowerName.endsWith(".pdf")) {
            try (PDDocument document = Loader.loadPDF(new RandomAccessReadBufferedFile(file))) {
                PDFRenderer renderer = new PDFRenderer(document);
                int pageCount = document.getNumberOfPages();
                pages = new BufferedImage[pageCount];
                for (int i = 0; i < pageCount; i++) {
                    pages[i] = renderer.renderImageWithDPI(i, 300); // 高质量渲染
                }
            }
        } else {
            BufferedImage image = ImageIO.read(file);
            pages = new BufferedImage[]{image};
        }

        // ===== 页面设置（A4 + 边距） =====
        PageFormat pageFormat = job.defaultPage();
        Paper paper = new Paper();
        double paperWidth = 595.0;  // A4 宽
        double paperHeight = 842.0; // A4 高
        double margin = 20;
        paper.setSize(paperWidth, paperHeight);
        paper.setImageableArea(margin, margin, paperWidth - 2 * margin, paperHeight - 2 * margin);
        pageFormat.setPaper(paper);

        // ===== 打印逻辑 =====
        job.setPrintable((graphics, format, pageIndex) -> {
            if (pageIndex >= pages.length) return Printable.NO_SUCH_PAGE;
            BufferedImage image = pages[pageIndex];

            Graphics2D g2d = (Graphics2D) graphics;
            g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            double areaWidth = format.getImageableWidth();
            double areaHeight = format.getImageableHeight();
            double areaX = format.getImageableX();
            double areaY = format.getImageableY();

            boolean rotate = image.getWidth() > image.getHeight();
            double imgWidth = image.getWidth();
            double imgHeight = image.getHeight();

            g2d.translate(areaX, areaY);

            if (rotate) {
                // 横向图片 → 旋转 90 度打印
                g2d.rotate(Math.toRadians(90));
                g2d.translate(0, -areaWidth); // 修正坐标系

                double scale = Math.min(areaHeight / imgWidth, areaWidth / imgHeight);
                double drawWidth = imgWidth * scale;
                double drawHeight = imgHeight * scale;

                double offsetX = (areaHeight - drawWidth) / 2;
                double offsetY = (areaWidth - drawHeight) / 2;

                g2d.drawImage(image, (int) offsetX, (int) offsetY, (int) drawWidth, (int) drawHeight, null);
            } else {
                // 竖向图片 → 正常打印
                double scale = Math.min(areaWidth / imgWidth, areaHeight / imgHeight);
                double drawWidth = imgWidth * scale;
                double drawHeight = imgHeight * scale;

                double offsetX = (areaWidth - drawWidth) / 2;
                double offsetY = (areaHeight - drawHeight) / 2;

                g2d.drawImage(image, (int) offsetX, (int) offsetY, (int) drawWidth, (int) drawHeight, null);
            }

            return Printable.PAGE_EXISTS;
        }, pageFormat);

        // ===== 打印属性 =====
        PrintRequestAttributeSet attr = new HashPrintRequestAttributeSet();
        attr.add(MediaSizeName.ISO_A4);
        attr.add(new Copies(1));

        // ===== 开始打印 =====
        job.print(attr);

        log.info("{} 已静默打印，页数：{}", file.getName(), pages.length);
    }



    public APIRs savePrinter(JzyhPrintParam jzyhPrintParam) {
        String workDir = System.getProperty("user.dir");
        String printerConfig = workDir + "\\printerConfig.txt";
        try {
            FileUtil.writeUtf8String(jzyhPrintParam.getPrinterName(), printerConfig);
        } catch (Exception e) {
            log.error("保存打印机失败：{}", e);
            e.printStackTrace();
            return APIRs.fail("保存失败");
        }
        return APIRs.success("保存成功");
    }
}
